#include "BTreeNode.h"
using namespace std;

/*
 * Read the content of the node from the page pid in the PageFile pf.
 * @param pid[IN] the PageId to read
 * @param pf[IN] PageFile to read from
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::read(PageId pid, const PageFile& pf)
{
	if(pf.read(pid, buffer) != 0)
		return -1004;
	return 0; }
    
/*
 * Write the content of the node to the page pid in the PageFile pf.
 * @param pid[IN] the PageId to write to
 * @param pf[IN] PageFile to write to
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::write(PageId pid, PageFile& pf)
{ 
	if(pf.write(pid, buffer) !=0)
		return -1005;
	return 0; }

/*
 * Return the number of keys stored in the node.
 * @return the number of keys in the node
 */
int BTLeafNode::getKeyCount()
{ 
	int key = 0;
	int max = 84;
	int counter = 0;
	for(int x = 0; x < 12*max; x=x+12)
	{
		counter = 0;
		for(int y = x; y < x+12; y++)
		{
			if(buffer[y] == '\0')
				counter++;
		}
		if(counter != 12)
			key++;
		else
			return key;
	}
	 return key; 
	
}

/*
 * Insert a (key, rid) pair to the node.
 * @param key[IN] the key to insert
 * @param rid[IN] the RecordId to insert
 * @return 0 if successful. Return an error code if the node is full.
 */
RC BTLeafNode::insert(int key, const RecordId& rid)
{ 
	//char keyChar[4];
	//char pidChar[4];
	//char sidChar[4];
	int kCount = getKeyCount();
	int eid;
	//int testing = sizeof(int);
	int maxsize = 84;
	int* keyptr = &key;
	RecordId temp;
	temp.pid = rid.pid;
	temp.sid = rid.sid;
	int* pidptr = &temp.pid;
	int* sidptr = &temp.sid;
	if(kCount == maxsize)
		return -1010;
	else
	{
		/*for(int a = 0; a < 4; a++)
		{
			keyChar[a] = (key >> (a * 8)) & 0xFF;
			pidChar[a] = (rid.pid >> (a * 8)) & 0xFF;
			sidChar[a] = (rid.sid >> (a * 8)) & 0xFF;
		}*/
		if(kCount != 0)
		{
			int location; //where to insert
			
			if(locate(key, eid) < 0)
			{
				location = 0 + 12 * kCount;
			}
			else
			{
				location = 0 + 12 * eid;
				/*for(int y = maxsize*12 - 1; y > location; y = y - 12)
				{
					for(int z = y; z > y - 12; z--)
					{
						buffer[z+12] = buffer[z];
					}
				}*/
				memmove(buffer+location+12, buffer+location, PageFile::PAGE_SIZE - location - 16); 
			}
			
			/*for(int zz = 0; zz < 4; zz++)
			{
				buffer[location + zz] = keyChar[zz];
				buffer[location + zz + 4] = pidChar[zz];
				buffer[location + zz + 8] = sidChar[zz];
			}*/
			memset(buffer+location, '\0', 12);
			memcpy(buffer+location, keyptr, 4);
			memcpy(buffer+location+4, pidptr, 4);
			memcpy(buffer+location+8, sidptr, 4);
			
		}
		else
		{
			/*
			for(int xx = 0; xx < 12; xx++)
			{
				if(xx < 4)
					buffer[xx] = keyChar[xx];
				else if (xx < 8)
					buffer[xx] = pidChar[xx];
				else
					buffer[xx] = sidChar[xx];
			}*/
			memcpy(buffer, keyptr, 4);
			memcpy(buffer+4, pidptr, 4);
			memcpy(buffer+4, sidptr, 4);
			/*for(int yy = 12; yy < 1024; yy++)
			{
				buffer[yy] = '\0';
			}*/
			memset(buffer+12, '\0', PageFile::PAGE_SIZE - 12);
		}
		
	return 0;
	}

}

/*
 * Insert the (key, rid) pair to the node
 * and split the node half and half with sibling.
 * The first key of the sibling node is returned in siblingKey.
 * @param key[IN] the key to insert.
 * @param rid[IN] the RecordId to insert.
 * @param sibling[IN] the sibling node to split with. This node MUST be EMPTY when this function is called.
 * @param siblingKey[OUT] the first key in the sibling node after split.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::insertAndSplit(int key, const RecordId& rid, 
                              BTLeafNode& sibling, int& siblingKey)
{ 
	int eid;
	locate(key, eid);
	int middleKey;
	RecordId middleRid;
	int splitID = ((getKeyCount()) / 2) + ((getKeyCount()) % 2);
	if(readEntry(splitID, middleKey, middleRid) != 0)
		return -1012;
	int insertKey;
	RecordId insertRid;
	RecordId temp;
	PageId tpid = getNextNodePtr();


	if (key > middleKey)
	{
		
		for(int x = splitID + 1; x < (getKeyCount()); x++)
		{
			if(readEntry(x, insertKey, insertRid) != 0)
				return -1012;
			sibling.insert(insertKey, insertRid);
		}
		sibling.insert(key, rid);
		if(sibling.readEntry(0, siblingKey, temp))
			return -1012;
		for(int y = (splitID + 1) * 12; y < 1020; y++)
		{
			buffer[y] = '\0';
		}
		setNextNodePtr(temp.pid);
	}
	else
	{
		for(int x = splitID; x < (getKeyCount()); x++)
		{
			readEntry(x, insertKey, insertRid);
			sibling.insert(insertKey, insertRid);
		}
		for(int y = splitID * 12; y < 1020; y++)
		{
			buffer[y] = '\0';
		}
		insert(key, rid);
		if(sibling.readEntry(0, siblingKey, temp))
			return -1012;
		setNextNodePtr(temp.pid);
	}
	sibling.setNextNodePtr(tpid);

	return 0; 

}

/*
 * Find the entry whose key value is larger than or equal to searchKey
 * and output the eid (entry number) whose key value >= searchKey.
 * Remeber that all keys inside a B+tree node should be kept sorted.
 * @param searchKey[IN] the key to search for
 * @param eid[OUT] the entry number that contains a key larger than or equalty to searchKey
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::locate(int searchKey, int& eid)
{
int* tempkey;
int tkey;
tempkey = &tkey;
int eidTrack = 0;

int kCount = getKeyCount();
	for(int x = 0; x < kCount*12; x = x + 12)
			{
			/*
			tempkey = 0;
			tempkey = (tempkey << 8) + buffer[x+3];
			tempkey = (tempkey << 8) + buffer[x+2];
			tempkey = (tempkey << 8) + buffer[x+1];
			tempkey = (tempkey << 8) + buffer[x];
			*/
			memcpy(tempkey, buffer+(12*eidTrack), 4);
			if(tkey >= searchKey)
			{
				eid = eidTrack;
				return 0;
			}
			eidTrack++;
			}
 return -1012;
}

/*
 * Read the (key, rid) pair from the eid entry.
 * @param eid[IN] the entry number to read the (key, rid) pair from
 * @param key[OUT] the key from the entry
 * @param rid[OUT] the RecordId from the entry
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::readEntry(int eid, int& key, RecordId& rid)
{ 
	//char keyChar[4];
	//char pidChar[4];
	//char sidChar[4];
	int tempkey;
	int temppid;
	int tempsid;
	int* tkey = &tempkey;
	int* tpid = &temppid;
	int* tsid = &tempsid;
	
	int kCount = getKeyCount();
	if(eid >= kCount) 
		return RC_NO_SUCH_RECORD;
	int location = 0 + 12 * eid;
	memcpy(tkey, buffer+location, 4);
	memcpy(tpid, buffer+location+4, 4);
	memcpy(tsid, buffer+location+8, 4);
	/*for(int a = 0; a < 4; a++)
	{
		keyChar[a] = buffer[location + a];
		pidChar[a] = buffer[location + a + 4];
		sidChar[a] = buffer[location + a + 8];
	}*/
	
	
	/*for(int y = 3; y >= 0; y--)
	{
		tempkey = (tempkey << 8) + keyChar[y];
		pid = (pid << 8) + pidChar[y];
		sid = (sid << 8) + sidChar[y];
	}*/
	key = tempkey;
	rid.pid = temppid;
	rid.sid = tempsid;
	return 0;
}

/*
 * Return the pid of the next slibling node.
 * @return the PageId of the next sibling node 
 */
PageId BTLeafNode::getNextNodePtr()
{ 
int tempkey = -1;
int* tkey = &tempkey;
/*for(int i = 1023; i > 1019; i--)
{
tempkey = 0;
tempkey = (tempkey << 8) + buffer[i];
}*/
memcpy(tkey, buffer+1020, 4);
 return tempkey;
}

/*
 * Set the pid of the next slibling node.
 * @param pid[IN] the PageId of the next sibling node 
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::setNextNodePtr(PageId pid)
{
if(pid < 0)
	return -1007;
//char pidChar[4];
/*for(int a = 0; a < 4; a++)
			{
				pidChar[a] = (pid >> (a * 8)) & 0xFF;
			}
for (int i = 0; i < 4; i++)
{
	buffer[((getKeyCount()) * 8) + i] = pidChar[i];
}*/
int* tpid = &pid;
memcpy(buffer+1020, tpid, 4);
 return 0;
}

/*
 * Read the content of the node from the page pid in the PageFile pf.
 * @param pid[IN] the PageId to read
 * @param pf[IN] PageFile to read from
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::read(PageId pid, const PageFile& pf)
{ 
	if(pf.read(pid, buffer) != 0)
		return -1004;
	return 0;
}
    
/*
 * Write the content of the node to the page pid in the PageFile pf.
 * @param pid[IN] the PageId to write to
 * @param pf[IN] PageFile to write to
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::write(PageId pid, PageFile& pf)
{
	if(pf.write(pid, buffer) !=0)
		return -1005;
	return 0;
}

/*
 * Return the number of keys stored in the node.
 * @return the number of keys in the node
 */
int BTNonLeafNode::getKeyCount()
{
/*int key = 0;
key = strlen(buffer)/8;
if ( key == 128)
	key = 127;
 return key; */
 
 
 int key = 0;
 int max = 127;
	int counter = 0;
	for(int x = 0; x < 8*max; x=x+8)
	{
		counter = 0;
		if(buffer[x] == '\0')
			counter++;
			
		if(counter != 8)
			key ++;
		else
			return key;
		
	}
	 return key; 
 
}


/*
 * Insert a (key, pid) pair to the node.
 * @param key[IN] the key to insert
 * @param pid[IN] the PageId to insert
 * @return 0 if successful. Return an error code if the node is full.
 */
RC BTNonLeafNode::insert(int key, PageId pid)
{
	int kCount = getKeyCount();
	int eid;
	//int testing = sizeof(int);
	int maxsize = 127;
	int* keyptr = &key;
	PageId temp;
	temp = pid;
	int* pidptr = &temp;
	
	if(kCount == maxsize)
		return -1010;
		
	else
	{
		/*for(int x = 0; x < 8*maxsize; x = x + 8)
		{
			for(int a = 0; a < 4; a++)
			{
				keyChar[a] = (key >> (a * 8)) & 0xFF;
				pidChar[a] = (pid >> (a * 8)) & 0xFF;
				
			}
			if(kCount != 0)
			{
				/*tempkey = 0;
				tempkey = (tempkey << 8) + buffer[x+3];
				tempkey = (tempkey << 8) + buffer[x+2];
				tempkey = (tempkey << 8) + buffer[x+1];
				tempkey = (tempkey << 8) + buffer[x];
				
				if (key <= tempkey)
				{
					for(int y = 1024 - 4 - 8 - 1; y > x; y = y - 4)
					{
						for(int z = y; z > y - 8; z--)
						{
							buffer[z+8] = buffer[z];
						}
					}
					for(int xx = 0; xx < 8; xx++)
					{
						if(xx < 4)
							buffer[x + xx] = pidChar[xx];
						else
							buffer[x + xx] = keyChar[xx];
					}
					return 0;
				}
			}
			else
			{
				for(int xx = 0; xx < 8; xx++)
				{
					if(xx < 4)
						buffer[xx] = pidChar[xx];
					else
						buffer[xx] = keyChar[xx];
				}
			}
		}*/
		//if(kCount != 0)
		//{
			int location; //where to insert
			PageId tempPid;
			if(locateChildPtr(key, tempPid) < 0)
			{
				location = 4 + 8 * kCount;
			}
			else
			{
				int checkPid;
				int* tpid = &checkPid;
				eid = 0;
				for(int y = 4; y < 4 + 8 * maxsize; y = y + 8)
				{
					memcpy(keyptr, buffer+y+4, 4);
					if(checkPid == tempPid)
						break;
					else
						eid++;						
				}
				location = 4 + 8 * eid;
				/*for(int y = maxsize*12 - 1; y > location; y = y - 12)
				{
					for(int z = y; z > y - 12; z--)
					{
						buffer[z+12] = buffer[z];
					}
				}*/
				memmove(buffer+location+8, buffer+location, PageFile::PAGE_SIZE - location - 16); 
				}
			
			/*for(int zz = 0; zz < 4; zz++)
			{
				buffer[location + zz] = keyChar[zz];
				buffer[location + zz + 4] = pidChar[zz];
				buffer[location + zz + 8] = sidChar[zz];
			}*/
			memset(buffer+location, '\0', 8);
			memcpy(buffer+location, keyptr, 4);
			memcpy(buffer+location+4, pidptr, 4);
			
			
		//}
		/*else
		{
			/*
			for(int xx = 0; xx < 12; xx++)
			{
				if(xx < 4)
					buffer[xx] = keyChar[xx];
				else if (xx < 8)
					buffer[xx] = pidChar[xx];
				else
					buffer[xx] = sidChar[xx];
			}
			memcpy(buffer, keyptr, 4);
			memcpy(buffer+4, pidptr, 4);
			memcpy(buffer+4, sidptr, 4);
			/*for(int yy = 12; yy < 1024; yy++)
			{
				buffer[yy] = '\0';
			}
			memset(buffer+12, '\0', PageFile::PAGE_SIZE - 12);
		}*/
	}
}

/*
 * Insert the (key, pid) pair to the node
 * and split the node half and half with sibling.
 * The middle key after the split is returned in midKey.
 * @param key[IN] the key to insert
 * @param pid[IN] the PageId to insert
 * @param sibling[IN] the sibling node to split with. This node MUST be empty when this function is called.
 * @param midKey[OUT] the key in the middle after the split. This key should be inserted to the parent node.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::insertAndSplit(int key, PageId pid, BTNonLeafNode& sibling, int& midKey)
{
	int splitID = ((getKeyCount()) / 2) + ((getKeyCount()) % 2);
	int midPidLocation = splitID * 8;
	int lastPidLocation = ((getKeyCount()) * 8);
	PageId cPid;
	PageId lPid;
	
	PageId* copyPid = &cPid;
	PageId* lastPid = &lPid;
	
	/*for(int x = 3; x >= 0; x--)
	{
		copyPid = (copyPid << 8) + buffer[midPidLocation + x];
		lastPid = (lastPid << 8) + buffer[lastPidLocation + x];
	}*/
	//char insertPid[4];
	//char insertKey[4];
	memcpy(copyPid, buffer+midPidLocation, 4);
	memcpy(lastPid, buffer+lastPidLocation, 4);
	for(int spot = midPidLocation; spot < ((getKeyCount()) * 8); spot++)
	{
		PageId iPid;
		int iKey;
		PageId* piPid = &iPid;
		int* piKey = &iKey;
		/*for(int y = 0; y < 4; y++)
		{
			insertPid[y] = buffer[spot + y];
			insertKey[y] = buffer[spot + 4 + y];
		}*/
		memcpy(piPid, buffer+spot, 4);
		memcpy(piKey, buffer+spot+4, 4);
		/*int tempPid = 0;
		int tempKey = 0;
		for(int z = 3; z >= 0; z--)
		{
			tempPid = (tempPid << 8) + insertPid[z];
			tempKey = (tempKey << 8) + insertKey[z];
		}*/
		if(spot == midPidLocation)
		{
			midKey = iKey;
		}
		
		sibling.insert(iKey, iPid);
		for(int del = spot; del < spot + 8; del++)
		{
			buffer[del] = '\0';
		}
	}
	sibling.setLastPid(lPid);
	setLastPid(cPid);
	return 0;
}

RC BTNonLeafNode::setLastPid(PageId pid)
{
/*char pidChar[4];
for(int a = 0; a < 4; a++)
{
	pidChar[a] = (pid >> (a * 8)) & 0xFF;
}
for (int i = 0; i < 4; i++)
{
	buffer[((getKeyCount()) * 8) + i] = pidChar[i];
}*/
PageId* ppid = &pid;
memcpy(buffer, ppid, 4);
 return 0;
}

/*
 * Given the searchKey, find the child-node pointer to follow and
 * output it in pid.
 * @param searchKey[IN] the searchKey that is being looked up.
 * @param pid[OUT] the pointer to the child node to follow.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::locateChildPtr(int searchKey, PageId& pid)
{ 
	int tempkey = -1;
	int temppid = -1;
	int* tkey = &tempkey;
	int* tpid = &temppid;
	for(int x = 4; x < 8*getKeyCount(); x = x + 8)
	{
		/*tempkey = 0;
		tempkey = (tempkey << 8) + buffer[x+3];
		tempkey = (tempkey << 8) + buffer[x+2];
		tempkey = (tempkey << 8) + buffer[x+1];
		tempkey = (tempkey << 8) + buffer[x];*/
		memcpy(tkey, buffer+x, 4);
		if(tempkey <= searchKey)
		{
			/*temppid = (temppid << 8) + buffer[x-1];
			temppid = (temppid << 8) + buffer[x-2];
			temppid = (temppid << 8) + buffer[x-3];
			temppid = (temppid << 8) + buffer[x-4];*/
			memcpy(tpid, buffer+x+4, 4);
			pid = temppid;
			return 0;
		}
	}
	/*
	temppid = (temppid << 8) + buffer[3];
	temppid = (temppid << 8) + buffer[2];
	temppid = (temppid << 8) + buffer[1];
	temppid = (temppid << 8) + buffer[0];*/

	/*memcpy(tpid, buffer, 4);
	pid = temppid;*/
	return RC_NO_SUCH_RECORD;
}

/*
 * Initialize the root node with (pid1, key, pid2).
 * @param pid1[IN] the first PageId to insert
 * @param key[IN] the key that should be inserted between the two PageIds
 * @param pid2[IN] the PageId to insert behind the key
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::initializeRoot(PageId pid1, int key, PageId pid2)
{ 
	/*char keyChar[4];
	char pid1Char[4];
	char pid2Char[4];
	for(int a = 0; a < 4; a++)
	{
		keyChar[a] = (key >> (a * 8)) & 0xFF;
		pid1Char[a] = (pid1 >> (a * 8)) & 0xFF;
		pid2Char[a] = (pid2 >> (a * 8)) & 0xFF;
	}
	for(int i = 0; i < 12; i++)
	{
		if(i < 4)
		{
			buffer[i] = pid1Char[i];
		}
		else if (i < 8)
		{
			buffer[i] = keyChar[i-4];
		} 
		else
			buffer[i] = keyChar[i-8];
	}*/
	PageId* ppid1 = &pid1;
	int* pkey = &key;
	PageId* ppid2 = &pid2;
	memcpy(buffer, ppid1, 4);
	memcpy(buffer+4, pkey, 4);
	memcpy(buffer+8, ppid2, 4);
	return 0; 
}
